package service;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import common.consts.ResponseStatus;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by Ace Yin on 2015/5/29 0029.
 */
public class ServiceResponse implements Serializable {
    private int status;
    private String message;
    @JsonUnwrapped
    private Map<String, Object> data;
    @JsonIgnore
    private Map<String, Object> tmp;

    private ServiceResponse(int status, String msg) {
        this.status = status;
        this.message = msg;
        this.data = new HashMap<>();
        this.tmp = new HashMap<>();
    }

    @JsonIgnore
    public boolean isSuccess() {
        return this.status == ResponseStatus.SUCCESS.code;
    }

    @JsonIgnore
    public boolean isFailed() {
        return !isSuccess();
    }

    public static ServiceResponse create(ResponseStatus status, String msg) {
        return new ServiceResponse(status.code, msg);
    }


    public <T> T getTemp(String name, Class<T> clzz) {
        if (name == null) {
            return null;
        }
        Object o = tmp.get(name);
        if (o == null) {
            return null;
        }

        Class<?> aClass = o.getClass();
        boolean sameClass = aClass.equals(clzz);
        if (!sameClass) {
            throw new RuntimeException("Data type not matches for key '" + name + "', expected type is '" + clzz.getName() + "' actual is '" + aClass.getName() + "'");
        }
        return (T) o;
    }

    /**
     * Get data with given type.
     *
     * @param name
     * @param clzz
     * @param <T>
     * @return
     */
    public <T> T get(String name, Class<T> clzz) {
        if (name == null) {
            return null;
        }
        Object o = data.get(name);
        if (o == null) {
            return null;
        }

        Class<?> aClass = o.getClass();
        boolean sameClass = aClass.equals(clzz);
        if (!sameClass) {
            throw new RuntimeException("Data type not matches for key '" + name + "', expected type is '" + clzz.getName() + "' actual is '" + aClass.getName() + "'");
        }
        return (T) o;
    }

    /**
     * Bind data to response.
     *
     * @param key
     * @param value
     * @return
     */
    public ServiceResponse with(String key, Object value) {
        this.data.put(key, value);
        return this;
    }

    public ServiceResponse withTemp(String key, Object value) {
        this.tmp.put(key, value);
        return this;
    }

    public static ServiceResponse success() {
        return success("");
    }

    public static ServiceResponse success(String msg) {
        ServiceResponse response = new ServiceResponse(ResponseStatus.SUCCESS.code, msg);
        return response;
    }

    public static ServiceResponse fail() {
        return fail("");
    }

    public static ServiceResponse fail(String msg) {
        return new ServiceResponse(ResponseStatus.FAIL.code, msg);
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @JsonAnyGetter
    public Map<String, Object> getData() {
        return data;
    }

    @JsonAnySetter
    public void setData(Map<String, Object> data) {
        this.data = data;
    }

}
